package com.my.study.nio;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * @author: yidujun
 * @create: 2021/04/06 19:19
 */
@Slf4j
public class NIOTest {

    /**
     * ByteBuffer支持类型化的put和get，
     * put放入的是什么数据类型，get就应该使用相应的数据类型来取出，否则可能有 BufferUnderflowException 异常
     */
    @Test
    public void nioByteBufferPutGetTest() {
        // 创建一个buffer
        ByteBuffer buffer = ByteBuffer.allocate(64);

        //类型化方式放入数据
        buffer.putInt(100);
        buffer.putLong(9);
        buffer.putChar('易');
        buffer.putShort((short) 4);

        System.out.println("position = " + buffer.position());
        System.out.println("limit = " + buffer.limit());
        System.out.println("capacity = " + buffer.capacity());

        System.out.println("-----");

        // 转化：读 -> 取
        buffer.flip();

        System.out.println("position = " + buffer.position());
        System.out.println("limit = " + buffer.limit());
        System.out.println("capacity = " + buffer.capacity());

        System.out.println("-----");

        System.out.println(buffer.getInt());
        System.out.println(buffer.getLong());
        System.out.println(buffer.getChar());
        System.out.println(buffer.getShort());
    }

    /**
     * 将普通 Buffer 转成只读 Buffer
     */
    @Test
    public void readOnlyBufferTest() {
        ByteBuffer buffer = ByteBuffer.allocate(64);

        for (int i = 0; i < 64; i++) {
            buffer.put((byte) i);
        }

        // 转化 写 -> 读
        buffer.flip();

        ByteBuffer readOnlyBuffer = buffer.asReadOnlyBuffer();
        System.out.println(readOnlyBuffer.getClass());

        // 读取
        while (readOnlyBuffer.hasRemaining()) {
            System.out.println(readOnlyBuffer.get());
        }

        // ReadOnlyBufferException异常
//        readOnlyBuffer.put(((byte) 1));
    }

    /**
     * NIO 还提供了 MappedByteBuffer，可以让文件直接在内存（堆外的内存）中进行修改
     */
    @Test
    public void mappedByteBufferTest() throws Exception {
        RandomAccessFile randomAccessFile = new RandomAccessFile("D:/1.txt", "rw");

        FileChannel channel = randomAccessFile.getChannel();

        /**
         * 参数 1：FileChannel.MapMode.READ_WRITE 使用的读写模式
         * 参数 2：0 可以直接修改的位置
         * 参数 3：5 是映射到内存的大小（不是索引位置），即 1.txt 的多少个字节映射到内存
         * 可以修改的范围就是 0-5
         * 实际类型 DirectByteBuffer
         */
        MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);

        mappedByteBuffer.put(0, ((byte) 'H'));
        mappedByteBuffer.put(2, ((byte) '9'));
        // IndexOutOfBoundsException
        mappedByteBuffer.put(5, ((byte) 'Y'));

        randomAccessFile.close();
        System.out.println("修改成功~");
    }

}
